<template>
  <Upload
    v-model:file-list="fileList"
    name="avatar"
    list-type="picture-card"
    class="avatar-uploader"
    :show-upload-list="false"
    :before-upload="beforeUpload"
    :custom-request="handleUpload"
  >
    <img v-if="imageUrl" :src="imageUrl" alt="avatar" />
    <div v-else>
      <loading-outlined v-if="loading" />
      <plus-outlined v-else />
      <div class="ant-upload-text">上传</div>
    </div>
  </Upload>
</template>
<script lang="ts">
  import { defineComponent, ref, nextTick, watch, onMounted } from 'vue';
  import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
  import { Upload } from 'ant-design-vue';
  import { useMessage } from '/@/hooks/web/useMessage';
  // types
  import { FileItem, UploadResultStatus } from './typing';
  import { basicProps } from './props';
  // utils
  import { checkImgType, getBase64WithFile } from './helper';
  import { buildUUID } from '/@/utils/uuid';
  import { isFunction } from '/@/utils/is';
  import { warn } from '/@/utils/log';
  import { useI18n } from '/@/hooks/web/useI18n';

  export default defineComponent({
    components: {
      LoadingOutlined,
      PlusOutlined,
      Upload,
    },
    props: {
      ...basicProps,
    },
    emits: ['change'],
    setup(props, { emit }) {
      const { t } = useI18n();
      const fileList = ref([]);
      const loading = ref<boolean>(false);
      const imageUrl = ref<string>();

      const { createMessage } = useMessage();

      onMounted(() => {
        imageUrl.value = props.currentValue;
      });

      watch(
        () => props.currentValue,
        async () => {
          nextTick(() => {
            imageUrl.value = props.currentValue;
          });
        },
        { flush: 'post' },
      );

      const beforeUpload = (file: File) => {
        if (!checkImgType(file)) {
          createMessage.error('不支持的文件类型!');
          return false;
        }
        const { maxSize } = props;
        // 设置最大值，则判断
        if (maxSize && file.size / 1024 / 1024 >= maxSize) {
          createMessage.error(t('component.upload.maxSizeMultiple', [maxSize]));
          return false;
        }
        loading.value = true;
        return true;
      };

      async function handleUpload({ file }) {
        const { size, name } = file;
        const item = {
          uuid: buildUUID(),
          file,
          size,
          name,
          percent: 0,
          type: name.split('.').pop(),
        } as FileItem;
        const data = await uploadApiByItem(item);
        if (data?.success) {
          getBase64WithFile(file).then(({ result: thumbUrl }) => {
            imageUrl.value = thumbUrl;
            loading.value = false;
            emit('change', item.responseData?.data);
          });
        }
      }

      async function uploadApiByItem(item: FileItem) {
        const { api } = props;
        if (!api || !isFunction(api)) {
          return warn('upload api must exist and be a function');
        }
        try {
          item.status = UploadResultStatus.UPLOADING;
          const { data } = await props.api?.(
            {
              data: {
                ...(props.uploadParams || {}),
              },
              file: item.file,
              name: props.name,
              filename: props.filename,
            },
            function onUploadProgress(progressEvent: ProgressEvent) {
              const complete = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
              item.percent = complete;
            },
          );
          item.status = UploadResultStatus.SUCCESS;
          item.responseData = data;
          return {
            success: true,
            error: null,
          };
        } catch (e) {
          console.log(e);
          item.status = UploadResultStatus.ERROR;
          return {
            success: false,
            error: e,
          };
        }
      }

      return {
        fileList,
        loading,
        imageUrl,
        beforeUpload,
        handleUpload,
      };
    },
  });
</script>
<style>
  .avatar-uploader > .ant-upload {
    width: 128px;
    height: 128px;
  }

  .ant-upload-select-picture-card i {
    font-size: 32px;
    color: #999;
  }

  .ant-upload-select-picture-card .ant-upload-text {
    margin-top: 8px;
    color: #666;
  }
</style>
